export function isInstance<T>(target: any, targetType: new (...args: any[]) => T, call?: (t: T) => void): target is T {
    if (target instanceof targetType) {
        call?.(target)
        return true
    }
    return false
}

export function notNull<T>(t: T | undefined | null, call: (t: T) => void) {
    if (t) {
        call(t)
    }
}

export function notNullR<T, R>(t: T | undefined | null, call: (t: T) => R): R | undefined {
    if (t) {
        return call(t)
    }
    return undefined
}

export function apply<T>(t: T, call: (t: T) => void): T {
    if (t) {
        call(t)
    }
    return t
}

export function withR<T, R>(t: T, call: (t: T) => R): R {
    return call(t)
}

export function repeat<T>(size: number, call: (index: number, result: T[]) => T, result: T[] = []): T[] {
    if (size === 0) return result
    for (let index = 0; index < size; index++) {
        result.push(call(index, result))
    }
    return result
}

export function iterator<T, R>(array: T[], call: (value: T, index: number, array: T[]) => (R | undefined)): R[] {
    const result: R[] = [];
    array.forEach((v, index, array) => {
        const value = call(v, index, array)
        if (value !== undefined && value !== null) {
            result.push(value);
        }
    })
    return result;
}